﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.UI;

namespace Tracker {
	public partial class Form1 : Form {
		private const int cviewers = 5;
		private readonly AutoResetEvent are = new AutoResetEvent(false);
		private readonly delProcessImage[] imgprocessors;
		private readonly List<Image<Bgr, byte>> imgsBuffer = new List<Image<Bgr, byte>>();
		private readonly List<Image<Bgr, byte>> imgsBuffer2 = new List<Image<Bgr, byte>>();
		private readonly ImageBox[] Viewers;

		private ICapture capture;
		private CircleF circle = new CircleF(new PointF(10, 10), 5);
		private int cnt;
		private bool doWork = true;
		private bool end;
		private MCvFont font;
		private Point headCenterBottom;
		private Point headCenterTop;
		private int headClick;
		private Image<Bgr, byte> imgBGR;
		private Image<Bgr, byte> imgBGRorg;
		private Image<Hsv, byte> imgHSV;
		private Image<Hsv, byte> imgHSVorg;
		private Image<Lab, byte> imgLAB;
		private Image<Bgr, byte> imgOverlay;
		private Image<Gray, byte> last;
		private Point lastCenter;
		private PointF[][] lastPs;
		private double medianIntensity;
		private double medianSaturation;
		private Rectangle rec;

		public Form1() {
			InitializeComponent();

			setupTable();
			Viewers = new ImageBox[cviewers * cviewers];
			imgprocessors = new delProcessImage[cviewers * cviewers];
			for (int i = 0; i < cviewers * cviewers; i++) {
				var ib = new ImageBox();
				ib.Dock = DockStyle.Fill;
				ib.MouseClick += Viewer_MouseClick;
				tableLayoutPanel1.Controls.Add(ib, i % cviewers, i / cviewers);
				Viewers[i] = ib;
			}

			imgprocessors[0] = xImgShowOriginal;
			imgprocessors[1] = xImgShowFrameCount;
			//imgprocessors[2] = xImgShowEdgeDetected;
			//imgprocessors[3] = xImgShowOriginalWithTrack;

			imgprocessors[5] = xImgShowH;
			imgprocessors[6] = xImgShowS;
			imgprocessors[7] = xImgShowI;
			imgprocessors[8] = xImgShowSI;
			//imgprocessors[9] = xImgHough;
			//imgprocessors[9] = xImgShowIGrad;

			//imgprocessors[10] = xImgShowSmoothed2ndDeviation;
			//imgprocessors[11] = xImgShowSEdgeDetection;
			//imgprocessors[12] = xImgShowIEdgeDetection;
			//imgprocessors[13] = xImgShowColDif2;
			//imgprocessors[14] = xImgShowColDif;

			imgprocessors[10] = xImgShowLABL;
			imgprocessors[11] = xImgShowLABA;
			imgprocessors[12] = xImgShowLABB;

			imgprocessors[15] = xImgShowR;
			imgprocessors[16] = xImgShowG;
			imgprocessors[17] = xImgShowB;

			//imgprocessors[20] = xImgHistEqulize;
			//imgprocessors[20] = xImgHough;
			imgprocessors[20] = xCVFM;
			imgprocessors[21] = xDFT1;
			imgprocessors[22] = xDFT2;
			imgprocessors[23] = xDFT3;

			init();
		}

		private void setupTable() {
			tableLayoutPanel1.RowStyles.Clear();
			tableLayoutPanel1.ColumnStyles.Clear();
			for (int i = 0; i < cviewers; i++) {
				tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, (((float)100) / cviewers)));
				tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, (((float)100) / cviewers)));
			}
			tableLayoutPanel1.ColumnCount = cviewers;
			tableLayoutPanel1.RowCount = cviewers;
		}

		private void init() {
			font = new MCvFont(FONT.CV_FONT_HERSHEY_COMPLEX, 1.0, 1.0);
			capture = new Capture(Directory.GetCurrentDirectory() + @"\..\..\..\..\..\..\099_Materials\Middle\xxx.avi");
			//capture = new Capture(0);
			//var imgcap = new ImageBasedCaptureDevice(){ DictionaryImagesPath=System.IO.Directory.GetCurrentDirectory() + @"\..\..\..\..\..\..\099_Materials\Middle\", ScaleFactor=0};
			//imgcap.LoadImages();
			//capture = imgcap;
			//new Thread(new ThreadStart(setNextImage)).Start();
			//new Thread(new ThreadStart(setNextImageFlager)).Start();
			//for (int i = 0; i < 1750; i++)
			//    capture.QueryFrame();
		}

		private bool LoadImg() {
			imgBGRorg = capture.QueryFrame();
			if (imgBGRorg == null) {
				return false;
			}
			if (imgOverlay == null) {
				imgOverlay = new Image<Bgr, byte>(imgBGRorg.Size);
			}
			cnt++;

			ProcessLoadedImage();
			return true;
			//Emgu.CV.OpticalFlow.
		}

		private void ProcessLoadedImage() {
			if (!doWork) {
				return;
			}
			if (imgBGRorg == null) {
				return;
			}
			imgBGR = imgBGRorg;
			//imgBGR = imgBGR.Add(new Bgr(75, 75, 75));
			if (chkEqHist.Checked) {
				//imgBGR[0]._EqualizeHist();
				//imgBGR[1]._EqualizeHist();
				//imgBGR[2]._EqualizeHist();
				var tmp = new Image<Bgr, byte>(new Size(imgBGRorg.Width, imgBGRorg.Height));
				tmp[0] = HistEqualize(imgBGRorg[0]);
				tmp[1] = HistEqualize(imgBGRorg[1]);
				tmp[2] = HistEqualize(imgBGRorg[2]);
				imgBGR = tmp;
			}

			if (chkBlur.Checked) {
				imgBGR = imgBGR.SmoothGaussian(hGausKernelSize.Value);
			}
			imgHSVorg = imgBGR.Convert<Hsv, byte>();
			imgHSV = imgHSVorg;
			//imgHSV[2] = HistEqualize(imgHSV[2]);

			imgLAB = imgBGR.Convert<Lab, byte>();

			for (int i = 0; i < imgprocessors.Length; i++) {
				if (imgprocessors[i] != null) {
					Viewers[i].Image = PostProcess(imgprocessors[i]());
				}
			}

			findHead();
		}

		private IImage xImgShowOriginal() {
			return imgBGR;
		}

		private IImage xImgShowOriginalWithTrack() {
			return imgBGR.Add(imgOverlay);
		}

		private IImage xImgShowFrameCount() {
			Image<Bgr, byte> img1 = imgBGR.Copy();

			//img1.Draw(rec, new Bgr(255, 255, 255), 2);
			//img1.Draw(new Ellipse(headCenter, ellipseSize, 0), new Bgr(255, 255, 255), 2);
			//img1.Draw(circle, new Bgr(255, 255, 255), 5);
			img1.Draw(GetFrameInfo(cnt), ref font, new Point(10, 50), new Bgr(255, 0, 0));

			return img1;
		}

		private IImage xImgShowEdgeDetected() {
			Image<Bgr, byte> i = imgBGR.Canny(imgBGR[headCenterTop], new Bgr(100, 100, 100));

			var j = new Image<Bgr, byte>(i.Size);
			for (int a = 0; a < i.Width; a++) {
				for (int b = 0; b < i.Height; b++) {
					Bgr p = i[b, a];
					if (p.Red == 0 && p.Blue == 0 && p.Green == 0) {
						continue;
					}
					if (p.Red == 255 && p.Blue == 255 && p.Green == 255) {
						j[b, a] = new Bgr(255, 255, 255);
					}
				}
			}
			return j; //img.SmoothGaussian(7).SmoothGaussian(7).PyrDown().PyrDown();

			//var imgWork = img.Copy().Convert<Gray, byte>();

			////imgWork = imgWork.SmoothGaussian(3);
			////var deriv = imgWork.Convolution(new ConvolutionKernelF(new float[,] { { -1, 0, 1 } }));
			//var deriv = imgWork.Convolution(new ConvolutionKernelF(new float[,] { { 0.08f, 0.17f, 0.08f }, { 0.17f, -1, 0.17f }, { 0.08f, 0.17f, 0.08f } }));
			//var val = int.Parse(textBox2.Text);
			////deriv = deriv.ThresholdBinary(new Gray(val), new Gray(255));

			//Viewer2.Image = deriv.Convert<byte>(delegate(float f) { return (byte)Math.Abs(f); });
		}

		private IImage xImgShowH() {
			Image<Gray, byte> img = imgHSV.Split()[0];
			for (int i = 0; i < img.Data.GetLength(0); i++) {
				for (int j = 0; j < img.Data.GetLength(1); j++) {
					if (img.Data[i, j, 0] > 127) {
						img.Data[i, j, 0] = (byte)(img.Data[i, j, 0] * -1 + 255);
					}
				}
			}
			return img;
		}

		private IImage xImgShowS() {
			return imgHSV.Split()[1];
		}

		private IImage xImgShowI() {
			return imgHSV.Split()[2];
		}

		private IImage xImgShowIGrad() {
			return
				imgHSV.Split()[2].Convolution(new ConvolutionKernelF(new float[,] { { -1, 0, 1 } })).Convert<Gray, byte>().
					ThresholdToZero(new Gray(20)).Not().ThresholdToZero(new Gray(20)).Not();
		}

		private IImage xImgShowSI() {
			Image<Gray, byte>[] tmp = imgBGR.Split();
			var newimg = new Image<Gray, int>(imgBGR.Size);

			newimg = newimg.Add(tmp[1].Convert<Gray, int>());
			newimg = newimg.Add(tmp[2].Convert<Gray, int>());
			newimg = newimg.Mul(0.5);
			return newimg;
		}

		private IImage xImgShowSmoothed2ndDeviation() {
			Image<Bgr, byte> imgWork = imgBGR.Copy();
			imgWork = imgWork.SmoothGaussian(3);
			Image<Bgr, float> deriv = imgWork.Convolution(new ConvolutionKernelF(new float[,] { { -2, 0, 2 } }));

			return deriv.Convert(delegate(float f) { return (byte)Math.Abs(f); });
		}

		private IImage xImgShowSEdgeDetection() {
			return imgHSV.Split()[1].Canny(new Gray(hScrollBar1.Value), new Gray(hScrollBar2.Value));
		}

		private IImage xImgShowIEdgeDetection() {
			return imgHSV.Split()[2].Canny(new Gray(hScrollBar1.Value), new Gray(hScrollBar2.Value));
		}

		private IImage xImgShowColDif() {
			imgsBuffer.Add(imgBGRorg);
			if (imgsBuffer.Count < 3) {
				return null;
			}
			Image<Bgr, byte> dif = imgsBuffer[imgsBuffer.Count - 2].Sub(imgsBuffer[imgsBuffer.Count - 3]);
			return imgsBuffer[imgsBuffer.Count - 1].Sub(dif);
		}

		private IImage xImgShowColDif2() {
			imgsBuffer2.Add(imgBGRorg);

			if (imgsBuffer2.Count < 3) {
				return null;
			}
			Image<Bgr, byte> dif = imgsBuffer2[imgsBuffer2.Count - 2].Sub(imgsBuffer2[imgsBuffer2.Count - 3]);
			return dif;
		}

		private IImage xImgShowR() {
			Image<Gray, byte> tmp = imgBGR.Split()[2];
			if (chkTreshold.Checked) {
				tmp = tmp.ThresholdBinary(new Gray(hScrollBar3.Value), new Gray(255));
			}
			return tmp;
		}

		private IImage xImgShowG() {
			Image<Gray, byte> tmp = imgBGR.Split()[1];
			if (chkTreshold.Checked) {
				tmp = tmp.ThresholdBinary(new Gray(hScrollBar3.Value), new Gray(255));
			}
			return tmp;
		}

		private IImage xImgShowB() {
			Image<Gray, byte> tmp = imgBGR.Split()[0];
			if (chkTreshold.Checked) {
				tmp = tmp.ThresholdBinary(new Gray(hScrollBar3.Value), new Gray(255));
			}
			return tmp;
		}

		private IImage xImgShowLABL() {
			Image<Gray, byte> tmp = imgLAB.Split()[0];
			//if (chkTreshold.Checked)
			//    tmp = tmp.ThresholdBinary(new Gray(hScrollBar3.Value), new Gray(255));
			return tmp;
		}

		private IImage xImgShowLABA() {
			Image<Gray, byte> tmp = imgLAB.Split()[1];
			//if (chkTreshold.Checked)
			//    tmp = tmp.ThresholdBinary(new Gray(hScrollBar3.Value), new Gray(255));
			return tmp;
		}

		private IImage xImgShowLABB() {
			Image<Gray, byte> tmp = imgLAB.Split()[2];
			//if (chkTreshold.Checked)
			//    tmp = tmp.ThresholdBinary(new Gray(hScrollBar3.Value), new Gray(255));
			return tmp;
		}

		private IImage xImgHistEqulize() {
			Image<Gray, byte>[] tmp = imgBGR.Split();
			var newimg = new Image<Gray, int>(imgBGR.Size);
			for (int i = 0; i < tmp.Length; i++) {
				newimg = newimg.Add(tmp[i].Convert<Gray, int>());
			}
			newimg = newimg.Mul(0.33);
			return newimg;
		}

		private IImage xImgHough() {
			var imgtmp = new Image<Gray, byte>(imgHSV.Size);
			foreach (var o in imgHSV[2].HoughCircles(new Gray(60), new Gray(20), 1, 50, 15, 100)) {
				foreach (CircleF oo in o) {
					imgtmp.Draw(oo, new Gray(255), 1);
				}
			}

			return imgtmp;
		}

		private IImage xDFT1() {
			return xDFT(0);
		}

		private IImage xDFT2() {
			return xDFT(1);
		}

		private IImage xDFT3() {
			return xDFT(2);
		}

		private IImage xDFT(int channel) {
			Image<Gray, float> imgIn = imgHSV[channel].Convert<Gray, float>();
			var res = new Image<Gray, float>(imgIn.Size);
			CvInvoke.cvDFT(imgIn.Ptr, res.Ptr, CV_DXT.CV_DXT_FORWARD | CV_DXT.CV_DXT_SCALE, 0);

			Image<Gray, byte> res2 = BringToByte(res);
			Image<Gray, byte> res3 = HistEqualize(res2);
			return res3;
		}

		private IImage xCVFM() {
			//return imgHSV.Laplace(1);

			PointF[][] ps = imgHSV.GoodFeaturesToTrack(1024, 0.01, 30, imgHSV.Height + 1);

			//var res = imgHSV.Copy().Convert<Gray, byte>();
			//foreach(var o in ps) {
			//    foreach(var oo in o) {
			//        res[(int)oo.Y, (int)oo.X] = new Gray(255);
			//    }
			//}
			//return res;
			Image<Gray, byte> working = imgHSV[2]; //.Convert<Gray, byte>();

			Image<Hsv, byte> res = null;
			var b = new byte[ps.Length];
			if (last != null) {
				var ia = new Image<Gray, float>(imgHSV.Size);
				var ib = new Image<Gray, float>(imgHSV.Size);
				//CvInvoke.cvCalcOpticalFlowPyrLK(last.Ptr, imgHSV.Ptr, IntPtr.Zero, IntPtr.Zero, lastPs, ps, ps.Length,
				//                                new Size(21, 21), 3, b, null, new MCvTermCriteria(10),
				//                                Emgu.CV.CvEnum.LKFLOW_TYPE.DEFAULT);
				//CvInvoke.cvCalcOpticalFlowLK(last.Ptr, working.Ptr, new Size(21, 21), ia.Ptr, ib.Ptr);
				CvInvoke.cvCalcOpticalFlowHS(last.Ptr, working.Ptr, 1, ia.Ptr, ib.Ptr, .001, new MCvTermCriteria(500));

				res = imgHSV.Copy();
				for (int i = 0; i < imgHSV.Height; i++) {
					for (int j = 0; j < imgHSV.Width; j++) {
						if (ia[i, j].Intensity > 0 || ib[i, j].Intensity > 0) {
							//res.Draw(new LineSegment2D(lastPs[i][j], ps[i][j]), new Hsv(0, 255, 100), 2);
							var p1 = new Point(i, j);
							var p2 = new Point((int)(i + ia[i, j].Intensity), (int)(j + ib[i, j].Intensity));

							if (p1 != p2 && Math.Abs(p1.X - p2.X) > 7) {
								res.Draw(new LineSegment2D(p1, p2), new Hsv(0, 255, 100), 2);
							}
						}
					}
				}
			}
			last = working;
			lastPs = ps;

			return res;
		}

		//Image<T, byte> GetAbs<S, T>(Image<T, byte> img) where T : struct, IColor {
		//    return img.Convert<byte>(delegate(S f) { return (byte)Math.Abs(f); });
		//}

		private Image<Gray, byte> BringToByte(Image<Gray, float> img) {
			var res = new Image<Gray, byte>(img.Size);
			byte[, ,] dataNew = res.Data;

			float min = 0, max = 0;

			float[, ,] data = img.Data;
			for (int i = 0; i < data.GetLength(0); i++) {
				for (int j = 0; j < data.GetLength(1); j++) {
					if (data[i, j, 0] < min) {
						min = data[i, j, 0];
					}
					if (data[i, j, 0] > max) {
						max = data[i, j, 0];
					}
				}
			}
			for (int i = 0; i < data.GetLength(0); i++) {
				for (int j = 0; j < data.GetLength(1); j++) {
					dataNew[i, j, 0] = (byte)(((data[i, j, 0] - min) / (max - min)) * 255);
				}
			}

			return res;
		}

		private Image<Gray, byte> HistEqualize(Image<Gray, byte> img) {
			//var iparts = img.Split();
			var h = new Histogram(256, new RangeF(0, 256));
			h.Accumulate(new IImage[] { img });
			h.Normalize(1);

			////return img.Mul(h.BackProject(new Image<Gray, byte>[] {img}));

			////return img;
			//var work = img.Copy();
			var work = new Image<Gray, byte>(img.Size);
			//int[] cnters = new int[256];
			var cntersnew = new double[256];
			var cntersnew2 = new int[256];

			cntersnew[0] = 255 * h[0];
			cntersnew2[0] = (int)cntersnew[0];
			for (int i = 1; i < 256; i++) {
				double nval = cntersnew[i - 1] + 255 * h[i];
				cntersnew[i] = nval;
				cntersnew2[i] = (int)nval;
			}

			//for (int i = 0; i < img.Width; i++)
			//    for (int j = 0; j < img.Height; j++) {
			//        cnters[(int)img[j, i].Intensity]++;
			//    }
			////float sump = 0;
			////for (int i = 0; i < 256; i++) {
			////    sump += cnters[i];
			////}

			//for (int i = 0; i < 256; i++) {
			//    for (int j = 0; j < i; j++)
			//        cntersnew[i] += (float)((double)cnters[j]) / (img.Width * img.Height);
			//}

			////float sum = 0;
			////for (int i = 0; i < 256; i++) {
			////    sum += cntersnew[i];
			////}

			//double prev = 0;
			//for (int i = 1; i < 256; i++) {
			//    var x = 255 * h[i];
			//    prev += x;
			//    cntersnew[i] = (float)( prev + (byte)x);
			//    //cntersnew2[i] = (int)(Math.Round());
			//    //cntersnew2[i] = (int)Math.Round(255 * cntersnew[i]);
			//}

			for (int i = 0; i < img.Width; i++) {
				for (int j = 0; j < img.Height; j++) {
					work[j, i] = new Gray(cntersnew2[(int)img[j, i].Intensity]);
				}
			}

			return work;
		}

		//Image<Gray, byte> GetEllipsePatternImage(int size) {
		//    var ret = new Image<Gray, byte>(size * 2 + 1, size * 4 + 1);
		//    ret.Draw(new Ellipse(new PointF((int)ret.Width / 2, (int)ret.Height / 2), new SizeF(ret.Width , ret.Height ), 0),
		//                        new Gray(255), 2);
		//    Viewer4.Image = ret;
		//    return ret;
		//}

		private IImage PostProcess(IImage img) {
			if (headCenterTop == Point.Empty || headCenterBottom == Point.Empty) {
				return img;
			}
			if (img == null) {
				return img;
			}

			var cvImg = new Image<Bgr, byte>(img.Bitmap);

			cvImg.Draw(
				new Rectangle(headCenterTop.X, headCenterTop.Y, headCenterBottom.X - headCenterTop.X,
							   headCenterBottom.Y - headCenterTop.Y), new Bgr(255, 255, 255), 1);

			return cvImg;
		}

		//Size ellipseSize;

		private void calcAverage(int x, int y, int width, int height, out double saturation, out double intensity) {
			saturation = 0;
			intensity = 0;
			for (int i = 0; i < width; i++) {
				for (int j = 0; j < height; j++) {
					saturation += imgHSV[y + j, x + i].Satuation;
					intensity += imgHSV[y + j, x + i].Value;
				}
			}
			saturation /= (height * width);
			intensity /= (height * width);
		}

		private void findHead() {
			if (headCenterTop == Point.Empty || headCenterBottom == Point.Empty) {
				return;
			}

			int searchWindow = 9;
			int headHeight = headCenterBottom.Y - headCenterTop.Y;
			int headWidth = headCenterBottom.X - headCenterTop.X;

			if (medianSaturation == 0) {
				calcAverage(headCenterTop.X, headCenterTop.Y, headWidth, headHeight, out medianSaturation, out medianIntensity);
				lastCenter = new Point(headCenterTop.X + headWidth / 2, headCenterTop.Y + headHeight / 2);
			}
			else {
				//calcAverage(headCenterTop.X, headCenterTop.Y, headWidth, headHeight,
				//    out medianSaturation, out medianIntensity);

				double rating = 10000000;
				var BestPositions = new List<Point>();

				double testSat, testInt;
				for (int i = -1 * searchWindow; i <= searchWindow; i++) {
					for (int j = -1 * searchWindow; j <= searchWindow; j++) {
						calcAverage(headCenterTop.X + i, headCenterTop.Y + j, headWidth, headHeight, out testSat, out testInt);
						double thisRating = Math.Sqrt(Math.Pow(medianSaturation - testSat, 2) + Math.Pow(medianIntensity - testInt, 2));
						if (thisRating < rating) {
							BestPositions.Clear();
							rating = thisRating;
							BestPositions.Add(new Point(i, j));
						}
						else if (thisRating == rating) {
							BestPositions.Add(new Point(i, j));
						}
					}
				}
				Point pTopLeft = new Point(), pBottomRight = new Point();
				foreach (Point p in BestPositions) {
					//if (pTopLeft.X == 0 || pTopLeft.X > p.X)
					pTopLeft.X += p.X;
					//if (pTopLeft.Y == 0 || pTopLeft.Y > p.Y)
					pTopLeft.Y += p.Y;

					//if (pBottomRight.X == 0 || pBottomRight.X < p.X)
					//pBottomRight.X += p.X;
					//if (pBottomRight.Y == 0 || pBottomRight.Y < p.Y)
					//pBottomRight.Y += p.Y;
				}
				headCenterTop.X += pTopLeft.X / BestPositions.Count; //(pBottomRight.X - pTopLeft.X);
				headCenterTop.Y += pTopLeft.Y / BestPositions.Count; //(pBottomRight.Y - pTopLeft.Y);

				headCenterBottom.X += pTopLeft.X / BestPositions.Count; //(pBottomRight.X - pTopLeft.X);
				headCenterBottom.Y += pTopLeft.Y / BestPositions.Count; //(pBottomRight.Y - pTopLeft.Y);

				var newCenter = new Point(headCenterTop.X + headWidth / 2, headCenterTop.Y + headHeight / 2);
				imgOverlay.Draw(new LineSegment2D(lastCenter, newCenter), new Bgr(255, 255, 0), 3);
				lastCenter = newCenter;
			}

			//for (int i = 0; i < 5; i++)
			//    for (int j = 0; j < 5; j++) {
			//        medianSaturation += imgHSV[headCenterTop.X - 2 + i, headCenterTop.Y - 2 + j].Satuation;
			//        medianIntensity += imgHSV[headCenterTop.X - 2 + i, headCenterTop.Y - 2 + j].Value;
			//    }

			//    ellipseSize = new Size(0, 0);
			//    textBox3.Text = "Target Ration:" + (((float)hScrollBar3.Value) / 100).ToString("0.000") + "\r\n";

			//    int vari = hScrollBar1.Value;
			//    var l = headCenter;

			//    var imgWorking = img.SmoothGaussian(7).SmoothGaussian(15);
			//    Viewer5.Image = imgWorking;

			//    Viewer6.Image = imgWorking.Convert<Gray,byte>();

			//    if (l.X > 0) {
			//        Bgr middlePoint = new Bgr();
			//        for (int i = 0; i < 9; i++)
			//            for (int j = 0; j < 9; j++)
			//                middlePoint = middlePoint.Add(imgWorking[l.Y - 4, l.X - 4]);
			//        middlePoint = new Bgr(middlePoint.Blue / 81, middlePoint.Green / 81, middlePoint.Red / 81);

			//        for (int i = 3; i < 200 / 2; i += 5) {
			//            int amountOfGoodPointsOnEllipse = 0;
			//            var imgEllipse = GetEllipsePatternImage(i);

			//            int amountOfPointsOnEllipse = 0;
			//            double blue = 0, red = 0, green = 0;
			//            for (int a = 0; a < imgEllipse.Width; a++)
			//                for (int b = 0; b < imgEllipse.Height; b++) {
			//                    if (imgEllipse[b, a].Intensity == 255) {
			//                        amountOfPointsOnEllipse++;

			//                        var imagePointOnEllipse = imgWorking[l.X - imgEllipse.Width / 2 + a, Math.Max(0, l.Y - imgEllipse.Height / 2 + b)];
			//                        int acnt = 0;
			//                        if (Math.Abs(middlePoint.Blue - imagePointOnEllipse.Blue) < hScrollBar2.Value) acnt++;
			//                        if (Math.Abs(middlePoint.Red - imagePointOnEllipse.Red) < hScrollBar2.Value) acnt++;
			//                        if (Math.Abs(middlePoint.Green - imagePointOnEllipse.Green) < hScrollBar2.Value) acnt++;
			//                        if (acnt > 2) {
			//                            amountOfGoodPointsOnEllipse++;
			//                        }
			//                    }
			//                }
			//            //blue = blue / amountOfPointsOnEllipse;
			//            //red = red / amountOfPointsOnEllipse;
			//            //green = green / amountOfPointsOnEllipse;

			//            //if (Math.Abs(img[l].Blue - blue) > hScrollBar2.Value
			//            //    || Math.Abs(img[l].Red - red) > hScrollBar2.Value
			//            //    || Math.Abs(img[l].Green - green) > hScrollBar2.Value) {
			//            //    ellipseSize = new Size(i * 2, i * 3);
			//            //    break;
			//            //}
			//            textBox3.Text += string.Format("Size: {0:000}; Points: {1:0000}; GoodPoint: {2:0000}; Ratio: {3:0.000}\r\n", i, amountOfPointsOnEllipse, amountOfGoodPointsOnEllipse,
			//                (float)amountOfGoodPointsOnEllipse / (float)amountOfPointsOnEllipse);
			//            float goodPointsTreshold = ((float)hScrollBar3.Value) / 100;
			//            if (!(amountOfGoodPointsOnEllipse > (float)amountOfPointsOnEllipse * goodPointsTreshold)) {
			//                //we no longer fulfill treshold, means outside face as many points are 'wrong'
			//                ellipseSize = new Size(i * 2 + 1, i * 4 + 1);
			//                break;
			//            }
			//        }
		}

		//    var pHead = img.SmoothGaussian(7).SmoothGaussian(7);
		//    var pOrg = pHead[l.Y, l.X];

		//    int xdelta = 0;
		//    while (l.X + xdelta < pHead.Width) {
		//        var p = pHead[l.Y, l.X + xdelta];
		//        int xcnt = 0;
		//        if (Math.Abs(pOrg.Blue - p.Blue) > vari) xcnt++;
		//        if (Math.Abs(pOrg.Green - p.Green) > vari) xcnt++;
		//        if (Math.Abs(pOrg.Red - p.Red) > vari) xcnt++;
		//        if (xcnt > 1)
		//            break;
		//        xdelta++;
		//    }

		//    int ydelta = 0;
		//    while (l.Y + ydelta < pHead.Height) {
		//        var p = pHead[l.Y + ydelta, l.X];
		//        int xcnt = 0;
		//        if (Math.Abs(pOrg.Blue - p.Blue) > vari) xcnt++;
		//        if (Math.Abs(pOrg.Green - p.Green) > vari) xcnt++;
		//        if (Math.Abs(pOrg.Red - p.Red) > vari) xcnt++;
		//        if (xcnt > 1)
		//            break;
		//        ydelta++;
		//    }

		//    rec = new Rectangle(new Point(l.X - xdelta, l.Y - ydelta), new Size(xdelta * 2, 2 * ydelta));

		//    //img.ROI = rec;
		//    //var c = img.FindContours();
		//    doFunction1();
		//    doFunction2();
		//    doFunction3();

		//}

		private void Viewer_MouseClick(object sender, MouseEventArgs e) {
			switch (headClick) {
				case 0:
					medianSaturation = 0;
					headCenterTop = e.Location;
					break;
				case 1:
					headCenterBottom = e.Location;
					findHead();
					break;
			}
			headClick++;

			if (headClick > 1) {
				headClick = 0;
			}

			//byte max =0;
			//for(int i=l.X-50;i<l.X+50;i++) {
			//    var val = img.Data[i,l.Y+100, 0];
			//    if (val > max)
			//        max = val;
			//}

			//var test = img.Data[e.X,e.Y, 0];

			//int xstart = Math.Max(e.X-50,0);
			//int xend = Math.Min(e.X+50, img.Width);
			//int[] xaxis = new int[xend-xstart];
			//for (int i = xstart; i < xend; i++) {
			//    xaxis[i] = xend - xstart;
			//}

			//circle = new CircleF(new PointF(l.X, l.Y), 10);
		}

		//void TEST   ()
		//            {

		//            var imgGray = img.Convert<Gray, byte>();
		//    var contRect = imgGray.FindContours();//.fir.dr..BoundingRectangle;
		//    //img.Draw(contRect, new Bgr(255, 255, 255), 2);

		//    //img = img.SmoothGaussian(3);
		//    //img = img.Mul(img);

		//    var deriv = img.Convolution(new ConvolutionKernelF(new float[,] { { -2, 0, 2 } }));
		//    //var img2 = deriv.Add(new Bgr(255, 255, 255));
		//    //var img3 = deriv.Mul(0.5);
		//    Viewer2.Image = deriv.Convert<byte>(delegate(float f) { return (byte)Math.Abs(f); });

		//    setimg();
		//}

		private void button1_Click(object sender, EventArgs e) {
			doWork = false;
			for (int i = 0; i < int.Parse(textBox1.Text) - 1; i++) {
				LoadImg();
			}
			doWork = true;
			LoadImg();
		}

		private string GetFrameInfo(int i) {
			int seconds = i / 24;
			int minutes = seconds / 60;

			return string.Format("{0:00}:{1:00}.{2:00}", minutes, seconds % 60, i % 24);
		}

		private void setNextImageFlager() {
			while (true) {
				Thread.Sleep(40);
				are.Set();
			}
		}

		private void setNextImage() {
			while (true) {
				are.WaitOne();
				LoadImg();
			}
		}

		private void button2_Click(object sender, EventArgs e) {
			LoadImg();
		}

		private void button3_Click(object sender, EventArgs e) {
			ProcessLoadedImage();
		}

		private void hScrollBar1_Scroll(object sender, ScrollEventArgs e) {
			ProcessLoadedImage();
		}

		private void hScrollBar2_Scroll(object sender, ScrollEventArgs e) {
			ProcessLoadedImage();
		}

		private void hScrollBar3_Scroll(object sender, ScrollEventArgs e) {
			ProcessLoadedImage();
		}

		private void chkEqHist_CheckedChanged(object sender, EventArgs e) {
			ProcessLoadedImage();
		}

		private void checkBox1_CheckedChanged(object sender, EventArgs e) {
			ProcessLoadedImage();
		}

		private void chkTreshold_CheckedChanged(object sender, EventArgs e) {
			ProcessLoadedImage();
		}

		private void button4_Click(object sender, EventArgs e) {
			//for (int i = 0; i < 10; i++)
			//    LoadImg();
			new Thread(run).Start();
		}

		private void run() {
			while (true) {
				LoadImg();
			}
		}

		#region Nested type: delProcessImage

		private delegate IImage delProcessImage();

		#endregion
	}
}